using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine.Jobs;
using Unity.Collections;
using Unity.Jobs;
using System.Linq;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Physics.Systems;

namespace LS
{
    [BurstCompile]
    [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    [UpdateBefore(typeof(PhysicsSystemGroup))]
    public partial struct RTSSelectionColorSystem : ISystem
    {
        private void OnUpdate(ref SystemState state)
        {
            var ecb = new EntityCommandBuffer(Allocator.Temp);
            foreach (var item in SystemAPI.Query<RefRO<RTSSelectionMarkComponentData>>().WithNone<PostMaterialColor>().WithEntityAccess())
            {
                ecb.AddComponent(item.Item2, new PostMaterialColor() { Value = new float4(0, 0, 1, 1) });
            }

            ecb.Playback(state.EntityManager);
            ecb.Dispose();

            new ChangeColorBySelectMark().Schedule();
        }
    }

    [BurstCompile]
    public partial struct ChangeColorBySelectMark : IJobEntity
    {
        public void Execute(RefRO<RTSSelectionMarkComponentData> mark, RefRW<PostMaterialColor> postColor)
        {
            postColor.ValueRW.Value = mark.ValueRO.Selected ? new float4(1, 0, 0, 1) : new float4(0, 0, 1, 1);
        }
    }

    [BurstCompile]
    public partial struct RTSSelectionSystem : ISystem
    {
        private void OnUpdate(ref SystemState state)
        {
            if (!SystemAPI.HasSingleton<RTSSelectionStateComponentData>())
            {
                return;
            }

            var inputData = SystemAPI.GetSingletonRW<RTSSelectionStateComponentData>();

            if (inputData.ValueRO.LeftClickInput.IsPressed)
            {
                InvokeSingleRaycast(ref state, ref inputData.ValueRW.LeftClickInput, ref inputData.ValueRW.LeftRaycastResult);
            }
            else if (inputData.ValueRO.SelectionPressed)
            {
                new MultiSelectionJob()
                {
                    bound = inputData.ValueRO.FrustumBounds,
                }
                .Schedule(state.Dependency)
                .Complete();
            }

            if (inputData.ValueRO.RightClickInput.IsPressed)
            {
                InvokeSingleRaycast(ref state, ref inputData.ValueRW.RightClickInput, ref inputData.ValueRW.RightRaycastResult);
            }
        }

        private void InvokeSingleRaycast(ref SystemState state, ref PlayerInputState inputState,ref RaycastResultResult result)
        {
            PhysicsWorldSingleton physicsWorld = SystemAPI.GetSingleton<PhysicsWorldSingleton>();

            var singleSelectionJob = new PhysicsRaycastJob()
            {
                PhysicsWorld = physicsWorld,
                SelectResult = new NativeReference<bool>(false, Allocator.TempJob),
                Hit = new NativeReference<Unity.Physics.RaycastHit>(Allocator.TempJob),
                RaycastInput = inputState.RaycastInputFromCamera
            };
            var singleSelectionJobHandler = singleSelectionJob.Schedule(state.Dependency);
            singleSelectionJobHandler.Complete();

            result.HasResult = singleSelectionJob.SelectResult.Value;
            result.RaycastResult = singleSelectionJob.Hit.Value;
            if (singleSelectionJob.SelectResult.Value)
            {
                new SingleSelectionResultJob()
                {
                    SelectedEntity = singleSelectionJob.Hit.Value.Entity
                }
                .Schedule(state.Dependency)
                .Complete();
            }
            singleSelectionJob.SelectResult.Dispose();
            singleSelectionJob.Hit.Dispose();
        }

        private void InvokeMultiSelection()
        {

        }
    }

    [BurstCompile]
    public partial struct MultiSelectionJob : IJobEntity
    {
        public FrustumBounds bound;

        public void Execute(ref RTSSelectionMarkComponentData selectionMark, ref LocalTransform transform)
        {
            for (int i = 0; i < 6; i++)
            {
                if (bound[i].SignedDistanceToPoint(transform.Position) <= 0)
                {
                    selectionMark.Selected = false;
                    return;
                }
            }
            selectionMark.Selected = true;
        }
    }

    [BurstCompile]
    public partial struct SingleSelectionResultJob : IJobEntity
    {
        [ReadOnly]
        public Entity SelectedEntity;

        public void Execute(ref RTSSelectionMarkComponentData selectionMark, Entity entity)
        {
            selectionMark.Selected = SelectedEntity == entity;
        }
    }

    [BurstCompile]
    public partial struct PhysicsRaycastJob : IJob
    {
        public PhysicsWorldSingleton PhysicsWorld;

        public NativeReference<Unity.Physics.RaycastHit> Hit;
        public NativeReference<bool> SelectResult;
        public Unity.Physics.RaycastInput RaycastInput;

        public void Execute()
        {
            if (PhysicsWorld.CastRay(RaycastInput, out var hit))
            {
                SelectResult.Value = true;
                Hit.Value = hit;
            }
            else
            {
                SelectResult.Value = false;
            }
        }
    }
}
